#include "nfc_management.h"
#include "internal.h"

struct supported_tag supported_tags[] = {
    { CLASSIC_1K,   "Mifare Classic 1k",            0x08, 0, 0, { 0x00 }, NULL },
    { CLASSIC_1K,   "Mifare Classic 1k (Emulated)", 0x28, 0, 0, { 0x00 }, NULL },
    { CLASSIC_1K,   "Mifare Classic 1k (Emulated)", 0x68, 0, 0, { 0x00 }, NULL },
    { CLASSIC_1K,   "Infineon Mifare Classic 1k",   0x88, 0, 0, { 0x00 }, NULL },
    { CLASSIC_4K,   "Mifare Classic 4k",            0x18, 0, 0, { 0x00 }, NULL },
    { CLASSIC_4K,   "Mifare Classic 4k (Emulated)", 0x38, 0, 0, { 0x00 }, NULL },
    { DESFIRE,      "Mifare DESFire",               0x20, 5, 4, { 0x75, 0x77, 0x81, 0x02 /*, 0xXX */ }, NULL},
    { DESFIRE,      "Cyanogenmod card emulation",   0x60, 4, 3, { 0x78, 0x33, 0x88 /*, 0xXX */ }, NULL},
    { DESFIRE,      "Android HCE",                  0x60, 4, 3, { 0x78, 0x80, 0x70 /*, 0xXX */ }, NULL},
    { ULTRALIGHT_C, "Mifare UltraLightC",           0x00, 0, 0, { 0x00 }, NULL },
    { ULTRALIGHT,   "Mifare UltraLight",            0x00, 0, 0, { 0x00 }, NULL },
};

/*
 * Automagically allocate a MifareTag given a device and target info.
 */
MifareTag
freefare_tag_new (nfc_device *device, nfc_iso14443a_info nai)
{
    bool found = false;
    struct supported_tag *tag_info;
    MifareTag tag;
    uint16_t atqa = 0;
    int i;

    //printf("szUidLen: %d\n", nai.szUidLen);
    //printf("abtUid: %u\n", nai.abtUid[0]);
    //printf("should be: %u\n", NXP_MANUFACTURER_CODE);
    //printf("btSak: %x\n", nai.btSak);
    //printf("nai.szAtsLen: %zu\n", nai.szAtsLen);
    //printf("abtAts: %d\n", nai.abtAts);

    /* Ensure the target is supported */


    for (i = 0; i < sizeof(nai.abtAtqa); i++)
	{
		atqa <<= 8;
		atqa |= nai.abtAtqa[i];
	}

	printf("atqa = %x\n", atqa);
	switch(atqa)
	{
		case ATQA_CLASSIC1K:
		// do tag stuff
		tag_info = &(supported_tags[0]);
		found = true;
		printf("Found supported tag: CLASSIC_1K\n");
		break;

		case ATQA_ULTRALIGHT:
		// do wristband stuff
		tag_info = &(supported_tags[10]);
		found = true;
		printf("Found supported tag: ULTRALIGHT\n");
		break;

		case ATQA_DESFIRE:
		// do phone stuff
		tag_info = &(supported_tags[6]);
		found = true;
		printf("Found supported tag: DESFIRE\n");
		break;

		default:
		found = false;
		printf("Could not find supported tag\n");
		break;
	}

	//tag_info = &(supported_tags[0]); //we did this
	//found = true; // we did this

    if (!found)
		return NULL;

	printf("tag type = %d\n", tag_info->type);
    /* Allocate memory for the found MIFARE target */
    switch (tag_info->type)
    {
		case CLASSIC_1K:
		case CLASSIC_4K:
		//tag = mifare_classic_tag_new ();
		break;
		case DESFIRE:
		printf("identified as desfire\n");
		tag = mifare_desfire_tag_new ();
		break;
		case ULTRALIGHT:
		case ULTRALIGHT_C:
		//tag = mifare_ultralight_tag_new ();
		break;
    }

    if (!tag)
		return NULL;

    /*
     * Initialize common fields
     * (Target specific fields are initialized in mifare_*_tag_new())
     */
    tag->device = device;
    tag->info = nai;
    //tag->active = 0;
    tag->tag_info = tag_info;

    return tag;
}

/*
 * Get a list of the MIFARE targets near to the provided NFC initiator.
 *
 * The list has to be freed using the freefare_free_tags() function.
 */
MifareTag *
freefare_get_tags (nfc_device *device)
{
    MifareTag *tags = NULL;
    int tag_count = 0;

    nfc_initiator_init(device);

    // Drop the field for a while
    nfc_device_set_property_bool(device,NP_ACTIVATE_FIELD,false);

    // Configure the CRC and Parity settings
    nfc_device_set_property_bool(device,NP_HANDLE_CRC,true);
    nfc_device_set_property_bool(device,NP_HANDLE_PARITY,true);
    nfc_device_set_property_bool(device,NP_AUTO_ISO14443_4,true);

    // Enable field so more power consuming cards can power themselves up
    nfc_device_set_property_bool(device,NP_ACTIVATE_FIELD,true);

    // Poll for a ISO14443A (MIFARE) tag
    printf("Polling for tag...\n");
    nfc_target candidates[MAX_CANDIDATES];
    int candidates_count;
    nfc_modulation modulation = {
	.nmt = NMT_ISO14443A,
	.nbr = NBR_106
    };
    printf("Checking for candidates...\n");
    if ((candidates_count = nfc_initiator_list_passive_targets(device, modulation, candidates, MAX_CANDIDATES)) < 0)
    {
		printf("No candidates found\n");
		return NULL;
	}

	printf("Candidates found: %d\n", candidates_count);

    tags = malloc(sizeof (void *));
    if(!tags) return NULL;
    tags[0] = NULL;

	int c;
    for (c = 0; c < candidates_count; c++) {
	MifareTag t;
	if ((t = freefare_tag_new(device, candidates[c].nti.nai))) {
	    /* (Re)Allocate memory for the found MIFARE targets array */
	    printf("Allocating space for tag\n");
	    MifareTag *p = realloc (tags, (tag_count + 2) * sizeof (MifareTag));
	    if (p)
		tags = p;
	    else
		return tags; // FAIL! Return what has been found so far.
	    tags[tag_count++] = t;
	    tags[tag_count] = NULL;
	    printf("tag_count = %d\n", tag_count);
	}
    }

	printf("Tags[0] = %d\n", tags[0]);

    return tags;
}

/*
 * Free the provided tag list.
 */
void
freefare_free_tags (MifareTag *tags)
{
    if (tags)
    {
		int i;
		for (i = 0; tags[i]; i++)
		{
			freefare_free_tag(tags[i]);
		}
	free (tags);
    }
}

/*
 * Free the provided tag.
 */
void
freefare_free_tag (MifareTag tag)
{
    if (tag) {
        switch (tag->tag_info->type) {
        case CLASSIC_1K:
        case CLASSIC_4K:
            //mifare_classic_tag_free (tag);
            break;
        case DESFIRE:
            mifare_desfire_tag_free (tag);
            break;
        case ULTRALIGHT:
        case ULTRALIGHT_C:
            //mifare_ultralight_tag_free (tag);
            break;
        }
    }
}

/*
 * Returns the type of the provided tag.
 */
enum mifare_tag_type
freefare_get_tag_type (MifareTag tag)
{
    return tag->tag_info->type;
}

/*
 * Returns the UID of the provided tag.
 */
char *
freefare_get_tag_uid (MifareTag tag)
{
    char *res;
    if ((res = malloc (2 * tag->info.szUidLen + 1))) {
	size_t i;
	for (i = 0; i < tag->info.szUidLen; i++)
	    snprintf (res + 2*i, 3, "%02x", tag->info.abtUid[i]);
    }
    return res;
}

/*
 * Returns the friendly name of the provided tag.
 */
const char *
freefare_get_tag_friendly_name (MifareTag tag)
{
    return tag->tag_info->friendly_name;
}


